/*
* Copyright 2015 Workday, Inc.
*
* This software is available under the MIT license.
* Please see the LICENSE.txt file in this project.
*/
package com.workday.postman.adapter;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Locale;
import java.util.Map;
/**
* The base class for all {@link ParcelableAdapter}s that wrap Maps. Subclasses need only delcare
* the concrete Map type they wrap (for parameter {@link T}), declare a matching constructor, and
* declare a public constant {@code CREATOR} of type {@link com.workday.postman.adapter
* .AbstractMapParcelableAdapter.Creator}. This base class will handle wrapping the Map and writing
* it to the Parcel.
*
* @param <T> The concrete Map type this ParcelableAdapter wraps.
*
* @author nathan.taylor
* @since 2015-08-28.
*/
public class AbstractMapParcelableAdapter<T extends Map> implements ParcelableAdapter<T> {
/**
* The base implementation of {@link android.os.Parcelable.Creator} for Map ParcelableAdapters.
* This base class will handle unwrapping the Map written to the Parcel.
*
* @param <M> The concrete Map type the ParcelableAdapter wraps.
* @param <A> The concrete ParcelableAdapter this Creator will create.
*/
public abstract static class Creator<M extends Map, A extends AbstractMapParcelableAdapter<M>>
implements Parcelable.Creator<A> {
@Override
public final A createFromParcel(Parcel source) {
final Parcelable[] keys =
source.readParcelableArray(ParcelableAdapter.class.getClassLoader());
final Parcelable[] values =
source.readParcelableArray(ParcelableAdapter.class.getClassLoader());
if (keys.length != values.length) {
final String message = String.format(Locale.US,
"Length of keys array (%d) does not match "
+ "length of values array (%d)",
keys.length,
values.length);
throw new IllegalStateException(message);
}
final M map = newMapInstance();
@SuppressWarnings("unchecked")
final Map<Object, Object> castedMap = (Map<Object, Object>) map;
for (int i = 0; i < keys.length; i++) {
final Object key = ParcelableAdapters.unwrapParcelable(keys[i]);
final Object val = ParcelableAdapters.unwrapParcelable(values[i]);
castedMap.put(key, val);
}
return newParcelableAdapterInstance(map);
}
/**
* Create a new instance of the Map type the ParcelableAdapter wraps.
*/
protected abstract M newMapInstance();
/**
* Create a new instance of the ParcelableAdapter from the provided Map.
*
* @param map The Map that was extracted from the parcel, with all items unwrapped. This
* will be the map that was returned in {@link #newMapInstance()}, so there is no need to
* create a new Map from this one.
*/
protected abstract A newParcelableAdapterInstance(M map);
}
private final T value;
public AbstractMapParcelableAdapter(T value) {
this.value = value;
}
@Override
public final int describeContents() {
return 0;
}
@Override
public final void writeToParcel(Parcel dest, int flags) {
final Parcelable[] keys = new Parcelable[value.size()];
final Parcelable[] values = new Parcelable[value.size()];
int i = 0;
@SuppressWarnings("unchecked")
final Map<Object, Object> castedMap = value;
for (Map.Entry entry : castedMap.entrySet()) {
keys[i] = ParcelableAdapters.asParcelable(entry.getKey());
values[i] = ParcelableAdapters.asParcelable(entry.getValue());
}
dest.writeParcelableArray(keys, flags);
dest.writeParcelableArray(values, flags);
}
@Override
public final T getValue() {
return value;
}
}